package com.sirun.ble.utils.sec;

import com.sirun.ble.utils.AwByteUtil;
import com.sirun.ble.utils.AwLog;

/**
 * @author : hzw
 * @date : 2025/1/13
 * ICCE数据封装处理
 *
 * APDU (ApplicationProtocolDataUnit--应用协议数据单元)
 * Payload(有效载荷)
 * RKE ( 遥控认证, 即远控 )
 * 透传文本帧格式 Header + Body + CheckSum
 *
 * Header: (5个字节)
 *      SOF + Length + Control + FSN
 *
 *      SOF: 1字节, 帧数据开始, 固定为0x5A
 *      Length: 2字节, 从Control到Body结束的长度, 不含CheckSum
 *      Control: 1字节, 协议控制 (保留 + 方向 + 加密 + 响应 + 分包)指示位 00001010
 *          预留扩展(保留): Bit5~7
 *          方向指示位Bit4: 区分消息是 request 或 response, 0(response) 1(request)
 *          加密指示位Bit3: 是否使用"会话密钥"对 Body 字段进行加密 0(不加密) 1(加密, 对整个Body的内容进行 AES128CBC加密)
 *          响应指示位Bit2: 区分同步或异步响应, 0(同步) 1(异步)
 *          分包指示位Bit1Bit0: 是否进行业务分包
 *                ==> 00(当前帧为完整包)
 *                ==> 01, 10, 11(分包的起始, 过程, 最后分帧)
 *       FSN: 分包时对数据帧的编号 0(不分包) 0~255(分包)
 *
 * Body:
 *      MessageID + CommandID + Payload
 *      Payload：即TLV数据序列化格式(Type或Tag + Length + Value)
 *
 *      MessageID: 1字节, 标识指令类型(取值1~255, 固定从1开始编号)
 *          0x01: Auth, 认证类数据(握手认证交互的APDU数据), request始终由车辆发送给移动端.
 *          0x02: Commond, 指令类数据(定位, 防中继, RKE等)
 *          0x03: Notification, 通知类数据
 *          0x04~0xFF: RFU (保留未来使用 Reserved For future Use)
 *      CommandID: 1字节, 标识具体指令(取值1~255, 固定从1开始编号)
 *      Payload: （Tag-Length-Value）该内容以 TLV 格式拼接组装，以便更好地扩展。
 *
 * CheckSum: 2字节
 *      对 Header + Body 进行CRC16(CCITT模式) 校验
 */
public class AwIcceUtil {

    /**
     * 获取包体数据
     * @param messageID
     * @param commandID
     * @param payload
     * @return
     */
    public static byte[] getBodyData(byte messageID, byte commandID, byte[] payload) {
        byte[] bodyBytes = AwByteUtil.insertByte(payload, 0, commandID);
        AwLog.d("getBodyData, bodyBytes1: " + AwByteUtil.printBytes(bodyBytes));
        bodyBytes = AwByteUtil.insertByte(bodyBytes, 0, messageID);
        AwLog.d("getBodyData, bodyBytes2: " + AwByteUtil.printBytes(bodyBytes));
        return bodyBytes;
    }

    /**
     * 获取header数据
     * @param body 包体
     * @param directionTag 方向指示位
     * @param encryptionTag 是否加密指示位
     * @param responseTag 响应指示位
     * @param subcontractTag 分包指示位
     * @param fsnTag FSN编号
     * @return
     */
    public static byte[] getHeaderData(byte[] body, String directionTag, String encryptionTag, String responseTag, String subcontractTag, int fsnTag) {
        int length = 2 + body.length;
        AwLog.d("getHeaderData, length: " + length);
        byte[] headerBytes = new byte[5];
        headerBytes[0] = 0x5A;
        headerBytes[1] = 0x00;
        headerBytes[2] = AwByteUtil.hexStringToBytes(Integer.toHexString(length))[0];
        StringBuffer sb = new StringBuffer();
        sb.append("000");
        sb.append(directionTag).append(encryptionTag).append(responseTag).append(subcontractTag);
        AwLog.d("getHeaderData control str: " + sb.toString());

        headerBytes[3] = AwByteUtil.hexStringToBytes(AwByteUtil.from2To16(sb.toString()))[0];
        headerBytes[4] = AwByteUtil.hexStringToBytes(Integer.toHexString(fsnTag))[0];
        AwLog.d("getHeaderData, result: " + AwByteUtil.printBytes(headerBytes));
        return headerBytes;
    }

    /**
     * 整合透传数据
     * @param header 头部数据
     * @param body 包体数据
     * @return
     */
    public static byte[] getCommData(byte[] header, byte[] body) {
        AwLog.d("getCommData, header: " + AwByteUtil.printBytes(header));
        AwLog.d("getCommData, body: " + AwByteUtil.printBytes(body));
        byte[] noCrcResByte = AwByteUtil.mergeBytes(header, body);
        AwLog.d("getCommData, mergeBytes noCrcResByte: " + AwByteUtil.printBytes(noCrcResByte));
        //计算CRC16
        byte crcByte = (byte) CRC16_XMODEM(noCrcResByte);
        AwLog.d("getCommData, crcByte: " + AwByteUtil.printBytes(new byte[] { crcByte }));
        byte[] resultBytes = new byte[noCrcResByte.length + 2];
        System.arraycopy(noCrcResByte, 0, resultBytes, 0, noCrcResByte.length);
        resultBytes[resultBytes.length - 2] = 0x00;
        resultBytes[resultBytes.length - 1] = crcByte;
        AwLog.d("getCommData, mergeBytes resultBytes: " + AwByteUtil.printBytes(noCrcResByte));
        return resultBytes;
    }

    public static int CRC16_XMODEM(byte[] buffer) {
        int wCRCin = 0x0000;
        int wCPoly = 0xA8B3;
        for (byte b : buffer) {
            for (int i = 0; i < 8; i++) {
                boolean bit = ((b >> (7 - i) & 1) == 1);
                boolean c15 = ((wCRCin >> 15 & 1) == 1);
                wCRCin <<= 1;
                if (c15 ^ bit)
                    wCRCin ^= wCPoly;
            }
        }
        wCRCin &= 0xffff;
        return wCRCin ^= 0x0000;
    }
}
